﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel.Syndication;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using NCMIS.Client;
using NCMIS.Extensions;
using NCMIS.ObjectModel;
using NCMIS.ObjectModel.MetaData;
using NCMIS.ObjectModel.Rest;
using NCMIS.Produce;
using NCMIS.ServiceModel.Syndication;

namespace Ncmis.Client.Explorer
{
    /// <summary>
    /// Represents the Main Window.
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields

        LoginWindow _loginWindow = new LoginWindow();

        private ObservableCollection<NameValue> _capabilities = new ObservableCollection<NameValue>();
        private ObservableCollection<NameValue> _properties = new ObservableCollection<NameValue>();
        private ObservableCollection<NameValue> _typeProperties = new ObservableCollection<NameValue>();
        private ObservableCollection<CmisObject> _selectedItems = new ObservableCollection<CmisObject>();
        private ObservableCollection<CmisName> _selectedItemChildrenNames = new ObservableCollection<CmisName>();
        private ObservableCollection<NameValue> _selectedItemChildrenProperties = new ObservableCollection<NameValue>();

        private RepositoryInfo _repositoryInfo;
        private RepositoryCapabilities _repositoryCapabilities;
        private Uri _rootFolderUri;
        private Uri _typesUri;
        //private string _selectedFolderId;
        private SyndicationItem _selectedEntry;
        
        private CmisObject cmisObjectChild; // Currently selected child (in Root Folder)
        Collection<CmisObject> cmisObjectChildren = new Collection<CmisObject>();

        private List<SyndicationItem> _folderChildrenItems = new List<SyndicationItem>();

        private Point startPoint;

        #endregion

        #region Properties

        public ObservableCollection<NameValue> Capabilities
        {
            get { return _capabilities; }
        }

        public ObservableCollection<NameValue> Properties
        {
            get { return _properties; }
        }

        public ObservableCollection<NameValue> TypeProperties
        {
            get { return _typeProperties; }
        }

        public ObservableCollection<CmisObject> SelectedItems
        {
            get { return _selectedItems; }
        }

        public ObservableCollection<CmisName> SelectedItemChildrenNames
        {
            get { return _selectedItemChildrenNames; }
        }

        public ObservableCollection<NameValue> SelectedItemChildrenProperties
        {
            get { return _selectedItemChildrenProperties; }
        }

        public ServiceDocument ServiceDocument
        {
            get;
            set;
        }

        public CmisWebRequest CmisWebRequest
        {
            get;
            set;
        }

        #endregion

        #region Constructors

        public MainWindow()
        {
            InitializeComponent();
        }

        #endregion

        #region Methods

        public void CreateFolder()
        {
            if (_selectedEntry != null)
            {
                string folderName;
                if ((folderName = GetFolderName()) != null)
                {
                    SyndicationLink downLink = CmisSyndicationItem.GetLink(_selectedEntry, CmisLinkRel.Down);
                    CmisFolder.CreateFolder(CmisWebRequest, folderName, downLink.Uri);
                }
            }
        }

        private string GetFolderName()
        {
            string folderName = null;
            InputWindow inputWindow = new InputWindow();
            inputWindow.Owner = this;
            inputWindow.Caption = "Create folder";
            inputWindow.Promt = "Folder name:";
            bool? ok = inputWindow.ShowDialog();
            if (ok == true)
            {
                folderName = inputWindow.Text;
            }
            return folderName;
        }

        public void PopulateServiceDocument()
        {
            ServiceDocument = CmisWebRequest.GetServiceDocument();
        }

        /// <summary>
        /// Gets the <see cref="NCMIS.ObjectModel.CmisObject"/> from a <see cref="System.ServiceModel.Syndication.SyndicationItem"/>
        /// object.
        /// </summary>
        /// <param name="item">The source item.</param>
        /// <returns>A <see cref="NCMIS.ObjectModel.CmisObject"/> object.</returns>
        private static CmisObject GetObject(SyndicationItem item)
        {
            CmisObject cmisObject = null;
            CmisProperties cmisProperties = null;

            foreach (SyndicationElementExtension extension in item.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "object"))
            {
                XElement objectElement = new XElement(CmisNs.XCmis + "object");
                objectElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                objectElement.Name = CmisNs.XCmis + "object";

                XElement objectPropertiesElement = new XElement(CmisNs.XCmis + "CmisProperties");
                objectPropertiesElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                objectPropertiesElement.Name = CmisNs.XCmis + "CmisProperties";

                XElement objectPropertiesElement2 = new XElement(CmisNs.XCmis + "properties");
                objectPropertiesElement2.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                objectPropertiesElement2.Name = CmisNs.XCmis + "properties";

                XNode xnode = XElement.ReadFrom(extension.GetReader());
                XElement xelement = (XElement)xnode;

                if (xelement.Name.NamespaceName.Equals(CmisNs.Cmisra))
                {
                    foreach (XNode node in xelement.Nodes())
                    {
                        if (node.NodeType == XmlNodeType.Element)
                        {
                            XElement tempElement = (XElement)node;
                            XName tempName = tempElement.Name;

                            if (tempElement.Name.NamespaceName.Equals(CmisNs.Cmis))
                            {
                                List<CmisProperty> propList = new List<CmisProperty>();

                                // When capabilites are read direct from the RepositoryInfo object the 
                                // DataContractSerializer.ReadObject method throws SerializationException:
                                // "Expecting state 'Element'.. Encountered 'Text' with name '', namespace ''."
                                // (Only for Alfresco's service document). Most probably the service document
                                // contains some space characters (due to indenting and making the XML human readable)
                                // that are not allowed by WCF DataContractSerializer.
                                if (!tempElement.Name.LocalName.Equals("properties"))
                                {
                                    objectElement.Add(node);
                                }
                                else
                                {
                                    XElement subEl = (XElement)node;
                                    foreach (XNode subNode in subEl.Nodes())
                                    {
                                        if (subNode.NodeType.Equals(XmlNodeType.Element))
                                        {
                                            XElement propEl = (XElement)subNode;
                                            objectPropertiesElement2.Add(subNode);

                                            CmisProperty prop = null;
                                            switch (propEl.Name.LocalName)
                                            {
                                                case ("propertyId"):
                                                    prop = new CmisPropertyId();
                                                    if (propEl.Attribute("displayName") != null)
                                                        prop.DisplayName = propEl.Attribute("displayName").Value;
                                                    if (propEl.Attribute("localName") != null)
                                                        prop.LocalName = propEl.Attribute("localName").Value;
                                                    if (propEl.Attribute("propertyDefinitionId") != null)
                                                        prop.PropertyDefinitionId = propEl.Attribute("propertyDefinitionId").Value;
                                                    if (propEl.Attribute("queryName") != null)
                                                        prop.QueryName = propEl.Attribute("queryName").Value;
                                                    if (propEl.Value != null)
                                                        prop.Value = propEl.Value;
                                                    break;
                                                default:
                                                case ("propertyString"):
                                                    prop = new CmisPropertyString();
                                                    if (propEl.Attribute("displayName") != null)
                                                        prop.DisplayName = propEl.Attribute("displayName").Value;
                                                    if (propEl.Attribute("localName") != null)
                                                        prop.LocalName = propEl.Attribute("localName").Value;
                                                    if (propEl.Attribute("propertyDefinitionId") != null)
                                                        prop.PropertyDefinitionId = propEl.Attribute("propertyDefinitionId").Value;
                                                    if (propEl.Attribute("queryName") != null)
                                                        prop.QueryName = propEl.Attribute("queryName").Value;
                                                    if (propEl.Value != null)
                                                        prop.Value = propEl.Value;
                                                    break;
                                            }
                                            propList.Add(prop);
                                        }
                                    }
                                }
                                cmisProperties = new CmisProperties() { Items = propList.ToArray() };
                            }
                        }
                    }
                    objectPropertiesElement.Add(objectPropertiesElement2);
                }

                cmisObject = ReadObject<CmisObject>(objectElement.CreateReader());
                CmisProperties properties = ReadObject<CmisProperties>(objectPropertiesElement.CreateReader());

                cmisObject.Properties = cmisProperties;
            }

            return cmisObject;
        }

        /// <summary>
        /// Gets the <see cref="NCMIS.ObjectModel.MetaData.TypeDefinition"/> from a <see cref="System.ServiceModel.Syndication.SyndicationItem"/>
        /// object.
        /// </summary>
        /// <param name="item">The source item.</param>
        /// <returns>A <see cref="NCMIS.ObjectModel.MetaData.TypeDefinition"/> object.</returns>
        private TypeDefinition GetTypeDefinition(SyndicationItem item)
        {
            TypeDefinition typeDefinition = null;

            foreach (SyndicationElementExtension extension in item.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "type"))
            {
                XElement typeElement = new XElement(CmisNs.XCmis + "type");
                typeElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                typeElement.Name = CmisNs.XCmis + "type";

                XElement propertyIdDefinitionElement = new XElement(CmisNs.XCmis + "propertyIdDefinition");
                propertyIdDefinitionElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                propertyIdDefinitionElement.Name = CmisNs.XCmis + "propertyIdDefinition";

                XNode xnode = XElement.ReadFrom(extension.GetReader());
                XElement xelement = (XElement)xnode;

                if (xelement.Name.NamespaceName.Equals(CmisNs.Cmisra))
                {
                    foreach (XNode node in xelement.Nodes())
                    {
                        if (node.NodeType == XmlNodeType.Element)
                        {
                            XElement tempElement = (XElement)node;
                            XName tempName = tempElement.Name;

                            if (tempElement.Name.NamespaceName.Equals(CmisNs.Cmis))
                            {
                                List<CmisProperty> propList = new List<CmisProperty>();

                                if (!tempElement.Name.LocalName.Equals("properties"))
                                {
                                    typeElement.Add(node);
                                }
                                else
                                {
                                    // TODO
                                }
                            }
                        }
                    }
                }
                typeDefinition = ReadObject<TypeDefinition>(typeElement.CreateReader());
            }

            return typeDefinition;
        }

        /// <summary>
        /// Populates the GUI controls with data from CMIS Atom feeds.
        /// </summary>
        public void InitializeGui()
        {
            PopulateProperties();
            PopulateCapabilities();
            PopulateRootFolder();
            PopulateTypes();
            SetStatusBar();

            // The handler is handled by XAML code, uncomment line to programatically add handler.
            //typeCollectionTreeView.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(treeViewItem_Expanded));
        }

        private void PopulateCapabilities()
        {
            //if (_repositoryInfo.Capabilities != null)
            if (_repositoryCapabilities != null)
            {
                _capabilities.Add(new NameValue()
                {
                    Name = "ACL",
                    Value = _repositoryCapabilities.Acl.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "All Versions Searchable",
                    Value = _repositoryCapabilities.AllVersionsSearchable.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "Changes",
                    Value = _repositoryCapabilities.Changes.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "Content Stream Updatability",
                    Value = _repositoryCapabilities.ContentStreamUpdatability.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "Join",
                    Value = _repositoryCapabilities.Join.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "Multifiling",
                    Value = _repositoryCapabilities.Multifiling.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "PWC Searchable",
                    Value = _repositoryCapabilities.PwcSearchable.ToString(),
                });
                _capabilities.Add(new NameValue()
                {
                    Name = "PWC Updatable",
                    Value = _repositoryCapabilities.PwcUpdatable.ToString(),
                });
            }
        }

        private void PopulateTypeDefinitions(TypeDefinition typeDefinition)
        {
            _typeProperties.Clear();
            _typeProperties.Add(new NameValue()
            {
                Name = "ID",
                Value = typeDefinition.Id ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Local Name",
                Value = typeDefinition.LocalName ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Local Namespace",
                Value = typeDefinition.LocalNamespace ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Display Name",
                Value = typeDefinition.DisplayName ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Query Name",
                Value = typeDefinition.QueryName ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Description",
                Value = typeDefinition.Description ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Base ID",
                Value = typeDefinition.BaseId.ToString() ?? "N/A", // TODO: Add parse method, e.g. CmisFolder -> "cmis:folder"
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Creatable",
                Value = typeDefinition.Creatable.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Fileable",
                Value = typeDefinition.Fileable.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "queryable",
                Value = typeDefinition.Queryable.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Full Text Indexed",
                Value = typeDefinition.FulltextIndexed.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Included in Supertype Query",
                Value = typeDefinition.IncludedInSupertypeQuery.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Controllable Policy",
                Value = typeDefinition.ControllablePolicy.ToString().ToPascalCase() ?? "N/A",
            });
            _typeProperties.Add(new NameValue()
            {
                Name = "Controllable ACL",
                Value = typeDefinition.ControllableAcl.ToString().ToPascalCase() ?? "N/A",
            });
        }

        private void PopulateProperties()
        {
            if (this.ServiceDocument.Workspaces.Count <= 0)
            {
                MessageBox.Show("Could not find any workspace element in service document.");
                return;
            }

            List<NameValue> properties = new List<NameValue>();
            Workspace workspace = this.ServiceDocument.Workspaces[0];

            foreach (SyndicationElementExtension extension in workspace.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "repositoryInfo"))
            {
                XElement repInfoElement = new XElement(CmisNs.XCmis + "repositoryInfo");
                repInfoElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                repInfoElement.Name = CmisNs.XCmis + "repositoryInfo";

                XElement capabilitiesElement = new XElement(CmisNs.XCmis + "RepositoryCapabilities");
                capabilitiesElement.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                capabilitiesElement.Name = CmisNs.XCmis + "RepositoryCapabilities";

                Console.WriteLine(repInfoElement.ToString());

                XNode xelement = XElement.ReadFrom(extension.GetReader());
                XElement xe2 = (XElement)xelement;
                xe2.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                xe2.Name = CmisNs.XCmis + xe2.Name.LocalName;

                SyndicationElementExtension ex2 = new SyndicationElementExtension(xelement);
                XElement xel = (XElement)xelement;

                xel.SetAttributeValue("xmlns", CmisNs.XCmis.NamespaceName);
                xel.Name = CmisNs.XCmis + xe2.Name.LocalName;

                // get children
                foreach (XNode node in xel.Nodes())
                {
                    if (node.NodeType == XmlNodeType.Element)
                    {
                        XElement tempElement = (XElement)node;
                        XName tempName = tempElement.Name;
                        //Console.WriteLine(String.Format("Name: {0}, Value: {1}", tempName.LocalName, tempElement.Value));
                        //Console.WriteLine(node.ToString());
                        // Avoid including extended elements not part of the NCMIS Data Contract.
                        if (tempElement.Name.NamespaceName.Equals(CmisNs.Cmis))
                        {
                            // When capabilites are read direct from the RepositoryInfo object the 
                            // DataContractSerializer.ReadObject method throws SerializationException:
                            // "Expecting state 'Element'.. Encountered 'Text' with name '', namespace ''."
                            // (Only for Alfresco's service document). Most probably the service document
                            // contains some space characters (due to indenting and making the XML human readable)
                            // that are not allowed by WCF DataContractSerializer.
                            if (!tempElement.Name.LocalName.Equals("capabilities"))
                            {
                                repInfoElement.Add(node); // NEW
                            }
                            else
                            {
                                capabilitiesElement.Add(node); // NEW
                            }
                        }
                    }
                }

                _repositoryInfo = ReadObject<RepositoryInfo>(repInfoElement.CreateReader());
                _repositoryCapabilities = ReadObject<RepositoryCapabilities>(capabilitiesElement.CreateReader());
                
                // TEMP
                if (_repositoryInfo == null) _repositoryInfo = new RepositoryInfo();

                _properties.Add(new NameValue()
                {
                    Name = "Repository ID",
                    Value = _repositoryInfo.RepositoryId ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Repository Name",
                    Value = _repositoryInfo.RepositoryName ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Repository Name",
                    Value = _repositoryInfo.RepositoryName ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Repository Description",
                    Value = _repositoryInfo.RepositoryDescription ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Vendor Name",
                    Value = _repositoryInfo.VendorName ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Product Name",
                    Value = _repositoryInfo.ProductName ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Product Version",
                    Value = _repositoryInfo.ProductVersion ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Root Folder ID",
                    Value = _repositoryInfo.RootFolderId ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "Latest Change Log Token",
                    Value = _repositoryInfo.LatestChangeLogToken ?? "N/A",
                });
                _properties.Add(new NameValue()
                {
                    Name = "CMIS Version Supported",
                    Value = _repositoryInfo.CmisVersionSupported.ToString(), // TODO: Handle null value
                });
                _properties.Add(new NameValue()
                {
                    Name = "Thin Client URI",
                    Value = _repositoryInfo.ThinClientUri ?? "N/A",
                });
            }
        }

        private void PopulateRootFolder()
        {
            SyndicationFeed feed = null;

            ResourceCollectionInfo rootFolderCollection = this.ServiceDocument.GetCollection(ServiceDocumentCollection.RootFolder);
            _rootFolderUri = rootFolderCollection.Link;
            feed = CmisWebRequest.GetSyndicationFeed(_rootFolderUri.AbsoluteUri);

            if (feed != null)
            {
                foreach (SyndicationItem item in feed.Items)
                {
                    TreeViewItem tviFolder = new TreeViewItem()
                    {
                        Tag = item,
                        Header = item.Title.Text
                    };

                    Collection<TreeViewItem> children = GetChildren(item.Links);
                    foreach (TreeViewItem childItem in children)
                    {
                        TreeViewItem childFolder = new TreeViewItem()
                        {
                            Tag = item,
                            Header = "Child"
                        };
                        tviFolder.Items.Add(childFolder);
                    }

                    rootFolderTreeView.Items.Add(tviFolder);
                }
            }
        }

        private void PopulateTypes()
        {
            SyndicationFeed feed = null;
            Workspace workspace = this.ServiceDocument.Workspaces[0];

            foreach (ResourceCollectionInfo collection in workspace.Collections)
            {
                foreach (SyndicationElementExtension extension in collection.ElementExtensions.Where<SyndicationElementExtension>(x => x.OuterName == "collectionType"))
                {
                    XNode xnode = XElement.ReadFrom(extension.GetReader());
                    XElement xelement = (XElement)xnode;

                    if (xelement.Name.NamespaceName.Equals(CmisNs.Cmisra))
                    {
                        if (xelement.Value.Equals("types"))
                        {
                            _typesUri = collection.Link;
                            feed = CmisWebRequest.GetSyndicationFeed(_typesUri.AbsoluteUri);
                        }
                    }
                }
            }

            if (feed != null)
            {
                foreach (SyndicationItem item in feed.Items)
                {
                    TreeViewItem tviFolder = new TreeViewItem()
                    {
                        Tag = item,
                        Header = item.Title.Text
                    };

                    foreach (SyndicationLink link in item.Links)
                    {
                        if (link.RelationshipType.Equals(CmisLinkRel.Down) && link.MediaType.Equals(CmisContentType.Feed))
                        {
                            TreeViewItem childFolder = new TreeViewItem()
                            {
                                Tag = item,
                                Header = "Child"
                            };
                            tviFolder.Items.Add(childFolder);
                        }
                    }

                    typeCollectionTreeView.Items.Add(tviFolder);
                }
            }
        }

        private void SetStatusBar()
        {
            messageTextBlock.Text = string.Format("Connected to {0}", CmisWebRequest.ServiceDocumentUri.AbsoluteUri);
        }

        /// <summary>
        /// Deserializes an XML element into a CLR object.
        /// </summary>
        /// <typeparam name="T">The <see cref="System.Type"/> to return.</typeparam>
        /// <param name="reader">The XML element reader to read from.</param>
        /// <returns>An object of type T.</returns>
        public static T ReadObject<T>(XmlReader reader)
        {
            T type;
            DataContractSerializer ser = new DataContractSerializer(typeof(T));
            try
            {
                type = (T)ser.ReadObject(reader, true);
                reader.Close();
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
                return default(T);
            }
            return type;
        }

        public void DeleteFile()
        {

        }

        /// <summary>
        /// Gets children from a <see cref="System.ServiceModel.Syndication.SyndicationLink"/> colllection.
        /// </summary>
        /// <param name="links">The <see cref="System.ServiceModel.Syndication.SyndicationLink"/> collection to get children from.</param>
        /// <returns>Collection of <see cref="System.Windows.Controls.TreeViewItem"/> objects.</returns>
        private Collection<TreeViewItem> GetChildren(Collection<SyndicationLink> links)
        {
            Collection<TreeViewItem> children = new Collection<TreeViewItem>();
            foreach (SyndicationLink link in links)
            {
                if (link.RelationshipType.Equals(CmisLinkRel.Down) && link.MediaType.Equals(CmisContentType.Feed))
                {
                    SyndicationFeed childrenFeed = CmisWebRequest.GetSyndicationFeed(link.Uri.AbsoluteUri);

                    foreach (SyndicationItem item in childrenFeed.Items)
                    {
                        TreeViewItem childItem = new TreeViewItem()
                        {
                            Tag = item,
                            Header = item.Title.Text
                        };
                        childItem.Items.Add(new TreeViewItem());
                        children.Add(childItem);
                    }
                }
            }
            return children;
        }

        private bool SaveStreamToFile()
        {
            bool result = false;
            int index = selectedItemListView.SelectedIndex;
            SyndicationItem entry = _folderChildrenItems[index];
            Collection<SyndicationLink> links = entry.Links;
            Uri contentUri = (entry.Content as UrlSyndicationContent).Url;

            Stream contentStream = CmisWebRequest.GetContentStream(contentUri);
            CmisObject cmisObject = CmisSyndicationItem.GetObject(entry);
            //string contentStreamFileName = cmisObject.Properties.GetItem(CmisPropertyDefinitionId.ContentStreamFileName).Value; // DELETE
            string contentStreamFileName = cmisObject.Properties.ContentStreamFileName.SingleValue;
            string fileExtension = System.IO.Path.GetExtension(contentStreamFileName);

            Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
            dialog.RestoreDirectory = true;
            dialog.FileName = contentStreamFileName;
            //dialog.DefaultExt = "*.png";
            //dialog.DefaultExt = string.Format("*.{0}", fileExtension);
            //dialog.Filter = "Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*";
            dialog.Filter = "All files (*.*)|*.*";
            Nullable<bool> dialogResult = dialog.ShowDialog();

            if (dialogResult == true)
            {
                string fileName = dialog.FileName;
                using (FileStream fs = new FileStream(fileName, FileMode.Create))
                {
                    int readCount;
                    var buffer = new byte[8192];
                    while ((readCount = contentStream.Read(buffer, 0, buffer.Length)) != 0)
                        fs.Write(buffer, 0, readCount);
                }
                contentStream.Dispose();
                dialogResult = true;
            }
            return result;
        }

        /// <summary>
        /// Shows the open file dialog, and gets the dialog result.
        /// </summary>
        /// <param name="filePath">The file path of the chosen file.</param>
        /// <returns>The open file dialog result.</returns>
        private bool? GetOpenFileDialogResult(out string filePath)
        {
            Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
            dialog.FileName = string.Empty;
            dialog.Filter = "All files (*.*)|*.*";
            bool? result = dialog.ShowDialog();
            filePath = dialog.FileName;
            return result;
        }

        /// <summary>
        /// Gets a <see cref="System.Byte"/> array from a file on disc, by its path.
        /// </summary>
        /// <param name="filePath">The path of the file to get bytes from.</param>
        /// <returns>A <see cref="System.Byte"/> array.</returns>
        private byte[] GetBytesFromFilePath(string filePath)
        {
            byte[] bytes = null;
            using (StreamReader sr = new StreamReader(filePath))
            {
                string s = sr.ReadToEnd();
                bytes = Encoding.ASCII.GetBytes(s);
            }
            return bytes;
        }

        private void UploadFile()
        {
            string filePath = string.Empty;
            if (GetOpenFileDialogResult(out filePath) == true)
            {
                byte[] bytes = GetBytesFromFilePath(filePath);
                string fileName = Path.GetFileName(filePath);
                Content content = CmisSyndicationItem.CreateCmisraContent(bytes, fileName);

                CmisProperties properties = new CmisProperties()
                {
                    Items = new CmisProperty[]
                    {
                        new CmisPropertyId()
                        {
                            PropertyDefinitionId = CmisPropertyDefinitionId.ObjectTypeId,
                            Value = new string[] { "cmis:document" }
                        }
                    }
                };

                CmisObject cmisObject = new CmisObject() { Properties = properties };

                CmisSyndicationItem entry = new CmisSyndicationItem()
                {
                   Id = string.Format("urn:uuid:{0}", fileName),
                   Title = new TextSyndicationContent(fileName),
                   LastUpdatedTime = new DateTimeOffset(DateTime.Now),
                };

                entry.Authors.Add(new SyndicationPerson() { Name = "NCMIS Explorer" });
                entry.AddContent(content);
                entry.AddObject(cmisObject);

                SyndicationLink downLink = CmisSyndicationItem.GetLink(_selectedEntry, CmisLinkRel.Down);

                CmisWebRequest.InvokeSyndicationFeed(downLink.Uri.AbsoluteUri, entry);
            }
        }

        #endregion

        #region Events

        private void fileMenuExitMenuItem_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void fileMenuNewFolderMenuItem_Click(object sender, RoutedEventArgs e)
        {
            CreateFolder();
        }

        private void fileMenuUploadFileMenuItem_Click(object sender, RoutedEventArgs e)
        {
            UploadFile();
        }

        

        private void menuItemDeleteFile_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedEntry != null)
            {
                MessageBoxResult result = MessageBox.Show("Are you sure you want to delete file?", "Delete File", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.Yes)
                {
                    int index = selectedItemListView.SelectedIndex;
                    SyndicationItem entry = _folderChildrenItems[index];

                    SyndicationLink selfLink = CmisSyndicationItem.GetLink(entry, CmisLinkRel.Self);
                    CmisWebRequest.InvokeSyndicationFeed(selfLink.Uri.AbsoluteUri, "DELETE");
                }
            }
        }

        private void menuItemGetContent_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedEntry != null)
            {
                if (SaveStreamToFile())
                {
                    MessageBox.Show("File was saved.");
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _loginWindow = new LoginWindow();
            _loginWindow.Owner = this;
            bool? loggedIn = _loginWindow.ShowDialog();
            if (loggedIn != true)
                Application.Current.Shutdown();
        }

        private void rootFolderTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            SyndicationItem item = null;
            TreeViewItem selectedItem = (TreeViewItem)rootFolderTreeView.SelectedItem;
            if (selectedItem != null)
            {
                // Enable file upload
                fileMenuUploadFileMenuItem.IsEnabled = true;

                _folderChildrenItems.Clear();

                item = (SyndicationItem)selectedItem.Tag;
                _selectedEntry = item;
                _selectedItems.Clear();

                CmisObject cmisObject = GetObject(item);
                if (cmisObject != null)
                {
                    _selectedItems.Add(cmisObject);
                    _selectedItemChildrenNames.Clear();

                    foreach (SyndicationLink link in item.Links)
                    {
                        if (link.RelationshipType.Equals(CmisLinkRel.Down) && link.MediaType.Equals(CmisContentType.Feed))
                        {
                            SyndicationFeed folderChildrenFeed = CmisWebRequest.GetSyndicationFeed(link.Uri.AbsoluteUri);
                            foreach (SyndicationItem folderChildrenItem in folderChildrenFeed.Items)
                            {
                                _folderChildrenItems.Add(folderChildrenItem);
                                cmisObjectChild = GetObject(folderChildrenItem);
                                cmisObjectChildren.Add(cmisObjectChild);
                                if (cmisObjectChild != null)
                                {
                                    //_selectedFolderId = cmisObject.Properties.ObjectId.SingleValue;

                                    // TODO: Rewrite this sequence in form of "cmisObject.Properties.Name.SingleValue"
                                    foreach (CmisProperty property in cmisObjectChild.Properties.Items)
                                    {
                                        if (property.PropertyDefinitionId.Equals(CmisPropertyDefinitionId.Name))
                                        {
                                            _selectedItemChildrenNames.Add(new CmisName() { Name = property.Value });
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // Disable file upload
                fileMenuUploadFileMenuItem.IsEnabled = false;
            }
        }

        private void treeViewItem_Expanded(object sender, RoutedEventArgs e)
        {
            TreeView source = sender as TreeView;
            string senderName = source.Name;
            //
            // Source: http://stackoverflow.com/questions/416593/how-do-i-hook-into-beforenodeexpand-and-afternodeexpand-for-a-wpf-treeview
            //

            // we will only go through with this if our children haven't been populated
            TreeViewItem sourceItem = e.OriginalSource as TreeViewItem;
            if ((sourceItem != null)
                && (sourceItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated))
            {
                SyndicationItem parent = sourceItem.Tag as SyndicationItem;

                // Will be null when root folder node is expanded
                if (parent == null)
                    return;

                // get down links (children)
                Collection<TreeViewItem> children = GetChildren(parent.Links);
                sourceItem.Items.Clear();
                foreach (TreeViewItem childItem in children)
                {
                    if (senderName.Equals("rootFolderTreeView"))
                    {
                        // if rootFolderTreeView .. if object.type != cmis:folder, dont att this item
                        CmisObject childObject = GetObject(childItem.Tag as SyndicationItem);
                        //CmisProperty baseTypeIdProperty = childObject.Properties.GetItem(CmisPropertyDefinitionId.BaseTypeId); // DELETE
                        CmisProperty baseTypeIdProperty = childObject.Properties.BaseTypeId;

                        if (baseTypeIdProperty.Value.Equals("cmis:folder"))
                        {
                            sourceItem.Items.Add(childItem);
                        }
                    }
                    else
                    {
                        sourceItem.Items.Add(childItem);
                    }
                }

                // create a handler that will check our children and reset the cursor when the ItemContainerGenerator has finished
                EventHandler itemsGenerated = null;
                itemsGenerated = delegate(object o, EventArgs args)
                {
                    // if the children are done being generated...
                    if ((o as ItemContainerGenerator).Status == GeneratorStatus.ContainersGenerated)
                    {
                        //(o as ItemContainerGenerator).StatusChanged -= itemsGenerated;  // we're done, so remove the handler
                        //sourceItem.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate    // asynchronous reset of cursor
                        //{
                        //    myWindow.Cursor = Cursors.Arrow;    // reset cursor
                        //    Debug.WriteLine("Expanded in " + (DateTime.Now - before));
                        //});
                        this.Cursor = Cursors.Arrow;
                    }
                };
                sourceItem.ItemContainerGenerator.StatusChanged += itemsGenerated;  // add the handler
                this.Cursor = Cursors.Wait;     // wait cursor
            }
            e.Handled = true;
        }

        private void typeCollectionTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            SyndicationItem item = null;
            TreeViewItem selectedItem = (TreeViewItem)typeCollectionTreeView.SelectedItem;
            if (selectedItem != null)
            {
                item = (SyndicationItem)selectedItem.Tag;

                TypeDefinition typeDefinition = GetTypeDefinition(item);
                if (typeDefinition != null)
                {
                    PopulateTypeDefinitions(typeDefinition);
                }
            }
        }

        private void selectedItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            int i = selectedItemListView.SelectedIndex;

            _selectedItemChildrenProperties.Clear();
            if (i >= 0)
            {
                foreach (CmisProperty property in cmisObjectChildren[i].Properties.Items)
                {
                    _selectedItemChildrenProperties.Add(
                        new NameValue()
                        {
                            Name = property.PropertyDefinitionId,
                            Value = property.Value
                        });
                }
            }
        }

        private void mainTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.Source is TabControl)
            {
                // index 3 = rootFolderTabItem
                if ((e.Source as TabControl).SelectedIndex.Equals(3))
                {
                    if (rootFolderTreeView.SelectedItem != null)
                    {
                        fileMenuUploadFileMenuItem.IsEnabled = true;
                    }
                    else
                    {
                        fileMenuUploadFileMenuItem.IsEnabled = false;
                    }
                }
                else
                {
                    fileMenuUploadFileMenuItem.IsEnabled = false;
                }
            }

            //MessageBox.Show("Current tab is...");
        }

        private void selectedItemListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            startPoint = e.GetPosition(null);
        }

        private void selectedItemListView_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            Point mousePos = e.GetPosition(null);
            Vector diff = startPoint - mousePos;

            if (e.LeftButton == MouseButtonState.Pressed && Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance && Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
            {
                ListView listView = sender as ListView;
                //ListViewItem listViewItem = FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
                ListViewItem listViewItem = new ListViewItem();

                // Get data behind the listview item
                //CmisName cmisName = (CmisName)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
                CmisName cmisName = new CmisName() { Name = "Test Object" };

                // Initialize the drag & drop operation
                DataObject dragData = new DataObject("CmisContent", cmisName);
                DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
            }
        }

        private void rootFolderTreeView_DragEnter(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent("CmisContent") || sender == e.Source)
            {
                e.Effects = DragDropEffects.None;
            }
        }

        private void rootFolderTreeView_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("CmisContent"))
            {
                CmisName cmisName = e.Data.GetData("CmisContent") as CmisName;
            }
        }

        #endregion

        #region Helpers

        // Helper to search up the VisualTree
        private static T FindAnchestor<T>(DependencyObject current) where T : DependencyObject
        {
            do
            {
                if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            }
            while (current != null);
            return null;
        }

        #endregion
    }
}